package com.aliyun.heiyu.common.util.sm2;

import com.aliyun.heiyu.common.source.PskDao;
import com.aliyun.heiyu.common.source.PskImpl;
import com.aliyun.heiyu.common.source.Psktable;
import com.sgitg.sgcc.sm.SM2;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;

import java.math.BigInteger;
import java.nio.charset.StandardCharsets;

/* 国密算法--SM2 */

public class SM2Util {
    private static SM2 sm2 = new SM2();
    private static byte[] pkey; // 公钥字节码
    private static byte[] skey; // 私钥字节码

    static {
        PskDao dao = new PskImpl();
        Psktable psk = dao.findOne("1");
        skey = ByteUtils.fromHexString(psk.getSkey());
        pkey = ByteUtils.fromHexString(psk.getPkey());
    }

    /**
     * 生成公私钥
     * @return
     */
    public static KeyPair generateKeyPair() {
        AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.generateKeyPair();

        BigInteger privateKey = ((ECPrivateKeyParameters)key.getPrivate()).getD();
        ECPoint publicKey = ((ECPublicKeyParameters)key.getPublic()).getQ();

        return new KeyPair(
            new String(Hex.encode(publicKey.getEncoded(false)), StandardCharsets.UTF_8),
            new String(Hex.encode(privateKey.toByteArray()), StandardCharsets.UTF_8)
        );
    }

    //加密成十六进制密文
    public static String encrypt(String data) {
        return HexUtil.byteToHex(encrypt(data,pkey));
    }

    //16进制密文解密
    public static String decrypt(String data) {
        return new String(decrypt(HexUtil.hexToByte(data),skey), StandardCharsets.UTF_8);
    }

    //16进制密文解密
    public static String decryptToHex(String data) {
        return ByteUtils.toHexString(decrypt(HexUtil.hexToByte(data),skey));
    }

    public static void main(String[] args){
        String ss = "04365377bd6a8f827c54dba9c0ad8bf56e1bbe0a44e2399863f42594a8717e74b0de5a1bb8fbc873a45f39eb842bba0511470d8b637ab9f378f48a31bb6883c31feb2cd8e636cf8036e98975323048496e6795be0f8567c0695131cc7746255bfeb5407b98092e72deedb6287b5cec07099340f295c4487955dacc88548322ec12";
        System.out.println(decryptToHex(ss));
    }

    protected static byte[] encrypt(String data, byte[] publicKey) {
        if (StringUtils.isBlank(data)) {
            return null;
        }

        SM2Cipher cipher = new SM2Cipher();

        // C1
        byte[] c1Bytes = new byte[65];
        ECPoint c1 = cipher.encryptInit(sm2, sm2.ecc_curve.decodePoint(publicKey));
        c1Bytes = c1.getEncoded(false);

        // C2
        byte[] c2Bytes = data.getBytes(StandardCharsets.UTF_8);
        cipher.encrypt(c2Bytes);

        // C3
        byte[] c3Bytes = new byte[32];
        cipher.doFinal(c3Bytes);

        byte[] encryptData = new byte[c1Bytes.length + c2Bytes.length + c3Bytes.length];
        System.arraycopy(c1Bytes, 0, encryptData, 0, c1Bytes.length);
        System.arraycopy(c2Bytes, 0, encryptData, c1Bytes.length, c2Bytes.length);
        System.arraycopy(c3Bytes, 0, encryptData, c1Bytes.length + c2Bytes.length, c3Bytes.length);

        return encryptData;
    }

    /**
     * 加密
     * @param data
     * @param publicKey
     * @return
     */
    public static byte[] encrypt(String data, String publicKey) {
        return encrypt(data, HexUtil.hexToByte(publicKey));
    }

    /**
     * 加密
     * @param data
     * @param publicKey
     * @return
     */
    public static byte[] encrypt(byte[] data, String publicKey) {
        return encrypt(new String(data, StandardCharsets.UTF_8), HexUtil.hexToByte(publicKey));
    }

    /**
     * 加密
     * @param data
     * @param publicKey
     * @return
     */
    public static String encryptToHexString(String data, String publicKey) {
        return HexUtil.byteToHex(encrypt(data, HexUtil.hexToByte(publicKey)), false);
    }

    /**
     * 加密
     * @param data
     * @param publicKey
     * @return
     */
    public static String encryptToHexString(byte[] data, String publicKey) {
        return HexUtil.byteToHex(encrypt(new String(data, StandardCharsets.UTF_8), HexUtil.hexToByte(publicKey)), false);
    }

    /**
     * 解密
     * @param encryptedData
     * @param privateKey
     * @return
     */
    protected static byte[] decrypt(byte[] encryptedData, byte[] privateKey) {
        if (ArrayUtils.isEmpty(encryptedData)) {
            return null;
        }

        SM2Cipher cipher = new SM2Cipher();

        // C1
        byte[] c1Bytes = new byte[65];
        System.arraycopy(encryptedData, 0, c1Bytes, 0, c1Bytes.length);
        ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes).normalize();

        // C3
        byte[] c3Bytes = new byte[32];
        System.arraycopy(encryptedData, encryptedData.length - 32, c3Bytes, 0, 32);

        // C2
        int c2Len = encryptedData.length - 65 - 32;
        byte[] c2Bytes = new byte[c2Len];
        System.arraycopy(encryptedData, 65, c2Bytes, 0, c2Len);

        cipher.decryptInit(new BigInteger(1, privateKey), c1);
        cipher.decrypt(c2Bytes);
        cipher.doFinal(c3Bytes);
        return c2Bytes;
    }

    /**
     * 解密
     * @param encryptedData
     * @param privateKey
     * @return
     */
    public static byte[] decrypt(byte[] encryptedData, String privateKey) {
        return decrypt(encryptedData, HexUtil.hexToByte(privateKey));
    }

    /**
     * 解密
     * @param encryptedData
     * @param privateKey
     * @return
     */
    public static String decryptToString(byte[] encryptedData, String privateKey) {
        return new String(decrypt(encryptedData, HexUtil.hexToByte(privateKey)), StandardCharsets.UTF_8);
    }

    /**
     * 解密
     * @param encryptedData
     * @param privateKey
     * @return
     */
    public static String decryptToString(String encryptedData, String privateKey) {
        return new String(decrypt(Hex.decode(encryptedData), HexUtil.hexToByte(privateKey)), StandardCharsets.UTF_8);
    }


}
